#include <QVariant>
#include <QMetaType>
#include <QDataStream>
#include <QMetaMethod>
#include <QQmlComponent>
#include "QQuickROData.h"

Q_DECLARE_METATYPE(ObjectDataPtr)

class ROHost;
class RONode;

void setROObjectData(QObject *obj, ObjectDataPtr data)
{
    obj->setProperty("__roData",QVariant::fromValue(data));
}

ObjectDataPtr getROObjectData(QObject *obj)
{
    if(!obj)
    {
        return nullptr;
    }
    return obj->property("__roData").value<ObjectDataPtr>();
}

void clearROObjectData(QObject *obj)
{
    obj->setProperty("__roData",{});
}

static bool g_inited = false;

QDataStream& operator << (QDataStream& s,const ROId& o){
    s << o.id;
    return s;
}

QDataStream& operator >> (QDataStream& s,ROId& o){
    s >> o.id;
    return s;
}

static void initializeROId(){
    qRegisterMetaType<ROId>();
}

ROId::ROId()
{

}

ROId::ROId(const ROId &other)
{
    this->id  = other.id;
}

void convertValueForNet(QVariant &value, const ToObjectId& func)
{
    if(!g_inited)
    {
        initializeROId();
        g_inited = true;
    }

    auto type = value.metaType();
    
    if(type.flags() & QMetaType::TypeFlag::IsPointer)
    {
        ROId id;
        id.id = func(value.value<QObject*>());
        value = QVariant::fromValue(id);
    }
    else if(type == QMetaType::fromType<QVariantMap>())
    {
        auto map = value.toMap();
        for(auto i:map.keys())
        {
            auto & v = map[i];
            convertValueForNet(v,func);
        }
        value = map;
    }
    else if(type == QMetaType::fromType<QVariantList>())
    {
        auto list = value.toList();

        for(auto& i:list)
        {
            convertValueForNet(i,func);
        }

        value = list;
    }
}

void convertValueFromNet(QVariant &value, const FromObjectId& func)
{
    if(!g_inited)
    {
        initializeROId();
        g_inited = true;
    }

    auto type = value.metaType();

    if(type == QMetaType::fromType<ROId>())
    {
        value = QVariant::fromValue(func(value.value<ROId>().id));
    }
    else if(type == QMetaType::fromType<QVariantMap>())
    {
        auto map = value.toMap();
        for(auto i:map.keys())
        {
            auto& v = map[i];
            convertValueFromNet(v,func);
        }
        value = map;
    }
    else if(type == QMetaType::fromType<QVariantList>())
    {
        auto list = value.toList();
        for(auto& i:list)
        {
            convertValueFromNet(i,func);
        }
        value = list;
    }
}

void invokeMethod(QObject* obj,QString name,const QVariantList& params){
    QGenericArgument args[10];

    for(int i = 0;i<params.size();i++)
    {
        args[i] = QGenericArgument("QVariant",&params[i]);
    }

    auto meta = obj->metaObject();
    meta->invokeMethod(obj,name.toUtf8(),args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]);
}

QObject *createObject(QQmlComponent *comp, QObject *parent, const QVariantMap &properties)
{
    QObject* ret = nullptr;
    comp->metaObject()->invokeMethod(comp,"createObject",Q_RETURN_ARG(QObject*,ret),Q_ARG(QObject*,parent),Q_ARG(QVariantMap,properties));

    return ret;
}
